#include <string.h>
#include "redcodeobject.h"
#include "redarrayobject.h"
#include "redstringobject.h"
#include "bytecode_buffer.h"

static void code_clear(RedObject *self)
{
	Red_UNREF(RedCode_Args(self));
	Red_UNREF(RedCode_Fname(self));
	Red_UNREF(RedCode_Frees(self));
	Red_UNREF(RedCode_VarArg(self));
	Red_UNREF(RedCode_Source(self));
	Red_UNREF(RedCode_Module(self));
}

static void code_release(RedObject *self)
{
	code_clear(self);
	bytecode_buffer_free(RedCode_Buffer(self), RedCode_Size(self));
	Red_RELEASE();
}

static void code_traverse(RedObject *self, tp_visit_fn_t visit, void *args)
{
	Red_VISIT(RedCode_Args(self));
	Red_VISIT(RedCode_Fname(self));
	Red_VISIT(RedCode_Frees(self));
	Red_VISIT(RedCode_VarArg(self));
	Red_VISIT(RedCode_Source(self));
	Red_VISIT(RedCode_Module(self));
}

RedTypeObject RedType_Code =
{
	RedType_INIT("code", RedCodeObject, RedType_Object)

	.tp_clear		= code_clear,
	.tp_release		= code_release,
	.tp_traverse	= code_traverse,
};

void RedCode_LoadSource(RedObject *self, rstring_t *source, RedObject *module)
{
	int i = 0;
	int j = 0;
	int n = source->length;
	char *s = source->content;

	Red_REF(module);
	RedCode_Module(self) = module;
	RedCode_Source(self) = RedArray_Create();

	for (;;)
	{
		/* search for '\n' or '\r\n' */
		while (i < n && s[i] != '\r' && s[i] != '\n')
			i++;

		if (i == n)
		{
			/* anything remains */
			RedObject *line = RedString_FromBufferAndSize(s + j, i - j);

			RedArray_Append(RedCode_Source(self), line);
			Red_UNREF(line);
			break;
		}
		else
		{
			if (s[i] == '\n')
			{
				i++;					/* '\n' */
			}
			else if (s[i] == '\r')
			{
				if (s[i + 1] != '\n')
					i++;				/* '\r' */
				else
					i += 2;				/* '\r\n' */
			}

			/* dump the line */
			RedObject *line = RedString_FromBufferAndSize(s + j, i - j);

			j = i;
			RedArray_Append(RedCode_Source(self), line);
			Red_UNREF(line);
		}
	}
}

RedObject *RedCode_WrapBytecode(rstring_t *fname, bytecode_t *code, int size)
{
	RedObject *result = RedObject_Create(&RedType_Code, NULL);

	if (!result)
		return NULL;

	RedCode_Size(result) = size;
	RedCode_Args(result) = NULL;
	RedCode_Fname(result) = RedString_FromBufferAndSize(fname->content, fname->length);
	RedCode_Frees(result) = NULL;
	RedCode_VarArg(result) = NULL;
	RedCode_Buffer(result) = code;
	RedCode_Source(result) = NULL;
	RedCode_Module(result) = NULL;

	/* the Red_UNREF will set result to NULL */
	if (!RedCode_Fname(result))
		Red_UNREF(result);

	return result;
}
